package com.ccf.business.knowledge.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ccf.business.knowledge.model.vo.*;
import com.google.common.base.Charsets;
import com.google.common.collect.Lists;
import com.common.utils.constant.Constants;
import com.common.utils.constant.FileType;
import com.ccf.business.knowledge.mapper.LabelDataRelationMapper;
import com.ccf.business.knowledge.model.LabelDataEntity;
import com.ccf.business.knowledge.model.LabelDataRelation;
import com.ccf.business.knowledge.model.vo.*;
import com.ccf.business.knowledge.service.ILabelDataEntityService;
import com.ccf.business.knowledge.service.ILabelDataRelationService;
import com.common.link.cache.AtlasCache;
import com.common.model.pojo.Query;
import com.common.model.result.CodeEnum;
import com.common.model.result.PageResult;
import com.service.mybatis.service.impl.SuperServiceImpl;
import com.service.mybatis.util.Condition;
import com.framework.minio.service.MinioService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 标注任务实体标记
 *
 * @author ccf
 * @date 2022-02-15 16:56:10
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class LabelDataRelationServiceImpl extends SuperServiceImpl<LabelDataRelationMapper, LabelDataRelation> implements ILabelDataRelationService {

    private final ILabelDataEntityService iLabelDataEntityService;

    private final MinioService minioService;
    /**
    * 列表
    * @param query
    * @return
    */
    @Override
    public PageResult<LabelDataRelation> findList(Query query){
        Page<LabelDataRelation> page = page(Condition.getPage(query), Condition.getQueryWrapper(query));
        return PageResult.<LabelDataRelation>builder().datas(page.getRecords()).code(CodeEnum.SUCCESS.getCode()).count(page.getTotal()).build();
    }

    @Override
    public List<LabelRelationVO> relationList(String dataId) {
        List<LabelRelationVO> relationList = Lists.newArrayList();
        List<LabelDataEntity> entities = iLabelDataEntityService.list(Wrappers.<LabelDataEntity>query()
                .lambda().eq(LabelDataEntity::getDataId, dataId));
        List<LabelDataRelation> relations = list(Wrappers.<LabelDataRelation>query()
                        .lambda().eq(LabelDataRelation::getDataId, dataId));
        Map<String, List<LabelDataRelation>> relationsMap =
                relations.stream().collect(Collectors.groupingBy(LabelDataRelation::getTypeId));
        final Map<String, LabelDataEntity> entityAndObject =
                entities.stream().collect(Collectors.toMap(LabelDataEntity::getId, Function.identity()));
        for (Map.Entry<String, List<LabelDataRelation>> entry: relationsMap.entrySet()) {
            String key = entry.getKey();
            List<LabelDataRelation> value = entry.getValue();
            LabelRelationVO labelRelationVO = new LabelRelationVO();
            labelRelationVO.setId(key);
            labelRelationVO.setName(AtlasCache.get(key));
            labelRelationVO.setRelationList(value.stream()
                    .map(e->new LabelBeginEndVO(getLabelContent(e.getBeginId(), entityAndObject),
                            getLabelContent(e.getEndId(), entityAndObject)))
                    .collect(Collectors.toList()));
            relationList.add(labelRelationVO);
        }
        return relationList;
    }

    private LabelContentVO getLabelContent(String id, Map<String, LabelDataEntity> entityAndObject){
        if (entityAndObject.containsKey(id)){
            LabelDataEntity labelDataEntity = entityAndObject.get(id);
            return new LabelContentVO(labelDataEntity.getId(), labelDataEntity.getContext(), labelDataEntity.getStartOffset(), labelDataEntity.getEndOffset());
        }
        return null;
    }

    @Override
    public void saveOrUpdateBatch(LabelDataVO labelData) {
        String id = labelData.getId();
        List<LabelRelationVO> relationTypeList = labelData.getRelationTypeList();
        List<LabelDataRelation> relations = Lists.newArrayList();
        for (LabelRelationVO labelRelationVO : relationTypeList) {
            String typeId = labelRelationVO.getId();
            List<LabelBeginEndVO> relationList = labelRelationVO.getRelationList();
            for (LabelBeginEndVO labelBeginEndVO : relationList) {
                LabelContentVO source = labelBeginEndVO.getSource();
                LabelContentVO target = labelBeginEndVO.getTarget();
                LabelDataRelation labelDataRelation = new LabelDataRelation();
                labelDataRelation.setDataId(id);
                labelDataRelation.setTypeId(typeId);
                labelDataRelation.setBeginId(source.getRandomClass());
                labelDataRelation.setEndId(target.getRandomClass());
                relations.add(labelDataRelation);
            }
        }
        List<LabelDataRelation> oldDatas = list(Wrappers.<LabelDataRelation>query()
                .lambda().eq(LabelDataRelation::getDataId, id));
        if (CollUtil.isNotEmpty(oldDatas)){
            removeByIds(
                    oldDatas.stream().map(e->e.getId()).collect(Collectors.toList()));
        }
        if (relations.size() > 0){
            saveBatch(relations);
        }
    }

    @Override
    public String relationTidy(String dataId, String data) {
        List<LabelDataEntity> entities = iLabelDataEntityService.list(Wrappers.<LabelDataEntity>query()
                .lambda().eq(LabelDataEntity::getDataId, dataId));
        HashMap<String, LabelDataEntity> entityOffsetMap = entities.stream()
                .collect(HashMap::new, (e, v) -> e.put(v.getId(), v), HashMap::putAll);
        List<LabelDataRelation> relations = list(Wrappers.<LabelDataRelation>query()
                .lambda().eq(LabelDataRelation::getDataId, dataId));
        List<LabelRelationDataVO> list = Lists.newArrayList();
        for (LabelDataRelation ldr : relations) {
            LabelRelationDataVO labelRelationDataVO =
                    covertRelationShip(ldr, entityOffsetMap, data);
            if (labelRelationDataVO == null){
                continue;
            }
            list.add(labelRelationDataVO);
        }
        if (list.size() == 0){
            return null;
        }
        String res = toCsv(list);

        return saveToMinio(res, LabelDataRelation.DEFAULT_SRHHTT_FOLDER, FileType.CSV, dataId);

    }

    /**
     * 保存到minio
     * @param data
     * @param folder
     * @param type
     * @return
     */
    private String saveToMinio(String data, String folder, FileType type, String dataId) {
        InputStream inputStream = new ByteArrayInputStream(data.getBytes(Charsets.UTF_8));
        String path = minioService.getBucketName() + Constants.SLASH + folder + Constants.SLASH + dataId + Constants.DOT + type.getValue();
        minioService.coverFile(path, inputStream, type.getValue());
        return path;
    }


    /**
     * 将关系转换为
     * @param relation
     * @param entityOffsetMap
     * @param context
     * @return
     */
    private LabelRelationDataVO covertRelationShip(LabelDataRelation relation,
                                            Map<String, LabelDataEntity> entityOffsetMap,
                                            String context) {
        LabelRelationDataVO lrdvo = new LabelRelationDataVO();
        String headId = relation.getBeginId();
        String tailId = relation.getEndId();

        LabelDataEntity head = entityOffsetMap.get(headId);
        LabelDataEntity tail = entityOffsetMap.get(tailId);
        if (head == null || tail == null){
            return null;
        }
        String typeId = relation.getTypeId();
        String typeName = AtlasCache.get(typeId);
        if (StrUtil.isBlank(typeName)){
            return null;
        }
        lrdvo.setRelationName(typeName);
        lrdvo.setHeadName(head.getContext());
        lrdvo.setTailName(tail.getContext());
        Integer tailOffset = tail.getStartOffset();
        Integer headOffset = head.getStartOffset();
        lrdvo.setTailOffset(tailOffset);
        lrdvo.setHeadOffset(headOffset);
        if (tailOffset < headOffset) {
            Integer tmp = headOffset;
            headOffset = tailOffset;
            tailOffset = tmp + head.getContext().length();
        } else {
            tailOffset += tail.getContext().length();
        }
        String beforeSub = StrUtil.subPre(context, headOffset);
        String sub = StrUtil.sub(context, headOffset, tailOffset);
        String endSub = StrUtil.subSuf(context, tailOffset);
        String sectionFlag = " ";
        String before = covertPause(beforeSub, sectionFlag);
        boolean isContains = StrUtil.containsAny(before, sectionFlag);
        int removePreLength = isContains ? before.lastIndexOf(sectionFlag) + 1 : 0;
        beforeSub = isContains ? StrUtil.subAfter(before, sectionFlag, true) : before;
        endSub = StrUtil.subBefore(covertPause(endSub, sectionFlag), sectionFlag, false);

        String sentence = beforeSub + sub + endSub;
        lrdvo.setSentence(sentence);
        //现在坐标 等于原有坐标-前面截取掉的长度
        lrdvo.setHeadOffset(lrdvo.getHeadOffset() - removePreLength);
        lrdvo.setTailOffset(lrdvo.getTailOffset() - removePreLength);
        return lrdvo;
    }

    private String toCsv(List<LabelRelationDataVO> list){
        StringBuffer sb = new StringBuffer();
        String header = LabelDataRelation.SRHHTT_HEADER;
        sb.append(header);
        sb.append((char) 10);
        list.stream().forEach(e -> {
            sb.append(Constants.DOUBLE_QUOTATION).append(e.getSentence()).append(Constants.DOUBLE_QUOTATION).append(",")
                    .append(e.getRelationName()).append(",")
                    .append(e.getHeadName()).append(",")
                    .append(e.getHeadOffset()).append(",")
                    .append(e.getTailName()).append(",")
                    .append(e.getTailOffset()).append((char) 10);
        });
        return sb.toString();
    }

    /**
     * 转换停顿
     *
     * @param str
     */
    private String covertPause(String str, String sectionFlag) {
        //断句标识
        char[] sectionChars = {',', '，', '。', '?', '？', '!', '.', ';'};
        str = StrUtil.replaceChars(str, sectionChars, sectionFlag);
        return str;
    }
}
